package com.wrupple.muba.desktop.client.bootstrap;

import java.util.Collection;
import java.util.Map;

import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.place.shared.PlaceHistoryHandler;
import com.google.gwt.user.client.Window;
import com.wrupple.muba.common.client.application.DataCallback;
import com.wrupple.muba.common.shared.SequentialProcess;
import com.wrupple.muba.common.shared.ServiceBus;
import com.wrupple.muba.desktop.client.bootstrap.state.LoadModuleActivities;
import com.wrupple.muba.desktop.client.bootstrap.state.ReadDesktopMetadata;
import com.wrupple.muba.desktop.client.bootstrap.state.RegisterDesktopData;
import com.wrupple.muba.desktop.client.events.DesktopInitializationDoneEvent;
import com.wrupple.muba.desktop.client.module.DesktopModule;
import com.wrupple.muba.desktop.client.module.ModuleController;
import com.wrupple.muba.desktop.domain.DesktopLoadingStateHolder;

/**
 * TODO : define the configuration in an xml and have the compiler create
 * implementations of this class (it looks very doable)
 * 
 * @author japi
 * 
 */
public abstract class AbstractDesktopLoader implements RunAsyncCallback {


	@Override
	public void onFailure(Throwable reason) {
		Window.prompt("You broke me, are you happy?", reason.toString());
	}

	@Override
	public void onSuccess() {

		/*
		 * Load Basic Strictures
		 */

		final DesktopModule factory = getDesktopFactory();
		ServiceBus serviceBus = factory.getDesktopModuleController().getServiceBus();
		final EventBus eventBus = factory.getDesktopModuleController().getEventBus();
		final  PlaceHistoryHandler historyHandler =factory.getDesktopModuleController().getPlaceHistoryHandler();
		
		/*
		 * Create Process
		 */
		SequentialProcess<DesktopLoadingStateHolder, DesktopLoadingStateHolder> loading = new SequentialProcess<DesktopLoadingStateHolder, DesktopLoadingStateHolder>();
		loading.setContext(factory.getDesktopLoadingContext());

		// register places
		ReadDesktopMetadata registerPlaces = factory.getPlaceRegister();
		loading.addState(registerPlaces);
		// load modules
		LoadModuleActivities moduleLoader = factory.getModuleLoader();
		Map<String, ModuleController> modules = getAssignedModules(serviceBus);
		Collection<ModuleController> moduleValues = modules.values();
		registerServices(serviceBus,moduleValues);
		moduleLoader.setDesktopModules(modules);
		
		loading.addState(moduleLoader);
		// register activities
		RegisterDesktopData registerActivities = factory.getActivityRegister();
		loading.addState(registerActivities);
		
		DesktopLoadingStateHolder grandNew = new DesktopLoadingStateHolder();
		loading.start(grandNew, new DataCallback<DesktopLoadingStateHolder>() {

			@Override
			public void execute() {
				// Fire Event
				eventBus.fireEvent(new DesktopInitializationDoneEvent());
				// Goes to the place represented on URL else default place
				historyHandler.handleCurrentHistory();
			}
		}, eventBus);
	}

	private void registerServices(ServiceBus serviceBus,
			Collection<ModuleController> moduleValues) {
		for(ModuleController controller : moduleValues){
			controller.registerServices(serviceBus);
		}
	}

	public abstract DesktopModule getDesktopFactory();

	/**
	 * Instantiates modules and adds them to a map.
	 * 
	 * In the map, the key of the module must be equal to it's corresponding
	 * slice in desktop configuration.
	 * 
	 * Also, please register any public modules in the Service bus, thank you!
	 * 
	 * @return
	 */
	public abstract Map<String, ModuleController> getAssignedModules(ServiceBus  bus);

}
